/*jslint unparam: true, browser: true, indent: 2 */

;
(function ($, window, document, undefined) {
    'use strict';

    Foundation.libs.forms = {
        name: 'forms',

        version: '4.0.0',

        settings: {
            disable_class: 'no-custom'
        },

        init: function (scope, method, options) {
            this.scope = scope || this.scope;

            if (typeof method === 'object') {
                $.extend(true, this.settings, method);
            }

            if (typeof method != 'string') {
                if (!this.settings.init) {
                    this.events();
                }

                this.assemble();

                return this.settings.init;
            } else {
                return this[method].call(this, options);
            }
        },

        assemble: function () {
            $('form.custom input[type="radio"]').not('[data-customforms="disabled"]')
                .each(this.append_custom_markup);
            $('form.custom input[type="checkbox"]').not('[data-customforms="disabled"]')
                .each(this.append_custom_markup);
            $('form.custom select').not('[data-customforms="disabled"]')
                .each(this.append_custom_select);
        },

        events: function () {
            var self = this;

            $(this.scope)
                .on('click.fndtn.forms', 'form.custom span.custom.checkbox', function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    self.toggle_checkbox($(this));
                })
                .on('click.fndtn.forms', 'form.custom span.custom.radio', function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    self.toggle_radio($(this));
                })
                .on('change.fndtn.forms', 'form.custom select:not([data-customforms="disabled"])', function (e) {
                    self.refresh_custom_select($(this));
                })
                .on('click.fndtn.forms', 'form.custom label', function (e) {
                    var $associatedElement = $('#' + self.escape($(this).attr('for')) + ':not([data-customforms="disabled"])'),
                        $customCheckbox,
                        $customRadio;
                    if ($associatedElement.length !== 0) {
                        if ($associatedElement.attr('type') === 'checkbox') {
                            e.preventDefault();
                            $customCheckbox = $(this).find('span.custom.checkbox');
                            //the checkbox might be outside after the label
                            if ($customCheckbox.length == 0) {
                                $customCheckbox = $(this).next('span.custom.checkbox');
                            }
                            //the checkbox might be outside before the label
                            if ($customCheckbox.length == 0) {
                                $customCheckbox = $(this).prev('span.custom.checkbox');
                            }
                            self.toggle_checkbox($customCheckbox);
                        } else if ($associatedElement.attr('type') === 'radio') {
                            e.preventDefault();
                            $customRadio = $(this).find('span.custom.radio');
                            //the radio might be outside after the label
                            if ($customRadio.length == 0) {
                                $customRadio = $(this).next('span.custom.radio');
                            }
                            //the radio might be outside before the label
                            if ($customRadio.length == 0) {
                                $customRadio = $(this).prev('span.custom.radio');
                            }
                            self.toggle_radio($customRadio);
                        }
                    }
                })
                .on('click.fndtn.forms', 'form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector', function (e) {
                    var $this = $(this),
                        $dropdown = $this.closest('div.custom.dropdown'),
                        $select = $dropdown.prev();

                    // make sure other dropdowns close
                    if (!$dropdown.hasClass('open'))
                        $(self.scope).trigger('click');

                    e.preventDefault();
                    if (false === $select.is(':disabled')) {
                        $dropdown.toggleClass('open');

                        if ($dropdown.hasClass('open')) {
                            $(self.scope).on('click.fndtn.forms.customdropdown', function () {
                                $dropdown.removeClass('open');
                                $(self.scope).off('.fndtn.forms.customdropdown');
                            });
                        } else {
                            $(self.scope).on('.fndtn.forms.customdropdown');
                        }
                        return false;
                    }
                })
                .on('click.fndtn.forms touchend.fndtn.forms', 'form.custom div.custom.dropdown li', function (e) {
                    var $this = $(this),
                        $customDropdown = $this.closest('div.custom.dropdown'),
                        $select = $customDropdown.prev(),
                        selectedIndex = 0;

                    e.preventDefault();
                    e.stopPropagation();

                    if (!$(this).hasClass('disabled')) {
                        $('div.dropdown').not($customDropdown).removeClass('open');

                        var $oldThis = $this
                            .closest('ul')
                            .find('li.selected');
                        $oldThis.removeClass('selected');

                        $this.addClass('selected');

                        $customDropdown
                            .removeClass('open')
                            .find('a.current')
                            .html($this.html());

                        $this.closest('ul').find('li').each(function (index) {
                            if ($this[0] == this) {
                                selectedIndex = index;
                            }

                        });
                        $select[0].selectedIndex = selectedIndex;

                        //store the old value in data
                        $select.data('prevalue', $oldThis.html());
                        $select.trigger('change');
                    }
                });

            this.settings.init = true;
        },

        append_custom_markup: function (idx, sel) {
            var $this = $(sel).hide(),
                type = $this.attr('type'),
                $span = $this.next('span.custom.' + type);

            if ($span.length === 0) {
                $span = $('<span class="custom ' + type + '"></span>').insertAfter($this);
            }

            $span.toggleClass('checked', $this.is(':checked'));
            $span.toggleClass('disabled', $this.is(':disabled'));
        },

        append_custom_select: function (idx, sel) {
            var self = Foundation.libs.forms,
                $this = $(sel),
                $customSelect = $this.next('div.custom.dropdown'),
                $customList = $customSelect.find('ul'),
                $selectCurrent = $customSelect.find(".current"),
                $selector = $customSelect.find(".selector"),
                $options = $this.find('option'),
                $selectedOption = $options.filter(':selected'),
                maxWidth = 0,
                liHtml = '',
                $listItems,
                $currentSelect = false;

            if ($this.hasClass(self.settings.disable_class)) return;

            if ($customSelect.length === 0) {
                var customSelectSize = $this.hasClass('small') ? 'small' :
                    $this.hasClass('medium') ? 'medium' :
                        $this.hasClass('large') ? 'large' :
                            $this.hasClass('expand') ? 'expand' : '';

                $customSelect = $('<div class="' + ['custom', 'dropdown', customSelectSize].join(' ') + '"><a href="#" class="selector"></a><ul /></div>');
                $selector = $customSelect.find(".selector");
                $customList = $customSelect.find("ul");
                liHtml = $options.map(function () {
                    return "<li>" + $(this).html() + "</li>";
                }).get().join('');
                $customList.append(liHtml);
                $currentSelect = $customSelect.prepend('<a href="#" class="current">' + $selectedOption.html() + '</a>').find(".current");
                $this
                    .after($customSelect)
                    .hide();

            } else {
                liHtml = $options.map(function () {
                    return "<li>" + $(this).html() + "</li>";
                })
                    .get().join('');
                $customList
                    .html('')
                    .append(liHtml);

            } // endif $customSelect.length === 0
            $customSelect.toggleClass('disabled', $this.is(':disabled'));
            $listItems = $customList.find('li');

            $options.each(function (index) {
                if (this.selected) {
                    $listItems.eq(index).addClass('selected');

                    if ($currentSelect) {
                        $currentSelect.html($(this).html());
                    }

                }
                if ($(this).is(':disabled')) {
                    $listItems.eq(index).addClass('disabled');
                }
            });

            //
            // If we're not specifying a predetermined form size.
            //
            if (!$customSelect.is('.small, .medium, .large, .expand')) {

                // ------------------------------------------------------------------------------------
                // This is a work-around for when elements are contained within hidden parents.
                // For example, when custom-form elements are inside of a hidden reveal modal.
                //
                // We need to display the current custom list element as well as hidden parent elements
                // in order to properly calculate the list item element's width property.
                // -------------------------------------------------------------------------------------

                $customSelect.addClass('open');
                //
                // Quickly, display all parent elements.
                // This should help us calcualate the width of the list item's within the drop down.
                //
                var self = Foundation.libs.forms;
                self.hidden_fix.adjust($customList);

                maxWidth = ( self.outerWidth($listItems) > maxWidth ) ? self.outerWidth($listItems) : maxWidth;

                Foundation.libs.forms.hidden_fix.reset();

                $customSelect.removeClass('open');

            } // endif

        },

        refresh_custom_select: function ($select) {
            var self = this;
            var maxWidth = 0,
                $customSelect = $select.next(),
                $options = $select.find('option');

            $customSelect.find('ul').html('');

            $options.each(function () {
                var $li = $('<li>' + $(this).html() + '</li>');
                $customSelect.find('ul').append($li);
            });

            // re-populate
            $options.each(function (index) {
                if (this.selected) {
                    $customSelect.find('li').eq(index).addClass('selected');
                    $customSelect.find('.current').html($(this).html());
                }
                if ($(this).is(':disabled')) {
                    $customSelect.find('li').eq(index).addClass('disabled');
                }
            });

            // fix width
            $customSelect.removeAttr('style')
                .find('ul').removeAttr('style');
            $customSelect.find('li').each(function () {
                $customSelect.addClass('open');
                if (self.outerWidth($(this)) > maxWidth) {
                    maxWidth = self.outerWidth($(this));
                }
                $customSelect.removeClass('open');
            });
        },

        toggle_checkbox: function ($element) {
            var $input = $element.prev(),
                input = $input[0];

            if (false === $input.is(':disabled')) {
                input.checked = ((input.checked) ? false : true);
                $element.toggleClass('checked');

                $input.trigger('change');
            }
        },

        toggle_radio: function ($element) {
            var $input = $element.prev(),
                $form = $input.closest('form.custom'),
                input = $input[0];

            if (false === $input.is(':disabled')) {
                $form.find('input[type="radio"][name="' + this.escape($input.attr('name')) + '"]').next().not($element).removeClass('checked');
                if (!$element.hasClass('checked')) {
                    $element.toggleClass('checked');
                }
                input.checked = $element.hasClass('checked');

                $input.trigger('change');
            }
        },

        escape: function (text) {
            return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
        },

        hidden_fix: {
            /**
             * Sets all hidden parent elements and self to visibile.
             *
             * @method adjust
             * @param {jQuery Object} $child
             */

            // We'll use this to temporarily store style properties.
            tmp: [],

            // We'll use this to set hidden parent elements.
            hidden: null,

            adjust: function ($child) {
                // Internal reference.
                var _self = this;

                // Set all hidden parent elements, including this element.
                _self.hidden = $child.parents().andSelf().filter(":hidden");

                // Loop through all hidden elements.
                _self.hidden.each(function () {

                    // Cache the element.
                    var $elem = $(this);

                    // Store the style attribute.
                    // Undefined if element doesn't have a style attribute.
                    _self.tmp.push($elem.attr('style'));

                    // Set the element's display property to block,
                    // but ensure it's visibility is hidden.
                    $elem.css({'visibility': 'hidden', 'display': 'block'});
                });

            }, // end adjust

            /**
             * Resets the elements previous state.
             *
             * @method reset
             */
            reset: function () {
                // Internal reference.
                var _self = this;
                // Loop through our hidden element collection.
                _self.hidden.each(function (i) {
                    // Cache this element.
                    var $elem = $(this),
                        _tmp = _self.tmp[i]; // Get the stored 'style' value for this element.

                    // If the stored value is undefined.
                    if (_tmp === undefined)
                    // Remove the style attribute.
                        $elem.removeAttr('style');
                    else
                    // Otherwise, reset the element style attribute.
                        $elem.attr('style', _tmp);

                });
                // Reset the tmp array.
                _self.tmp = [];
                // Reset the hidden elements variable.
                _self.hidden = null;

            } // end reset

        },

        off: function () {
            $(this.scope).off('.fndtn.forms');
        }
    };
}(Foundation.zj, this, this.document));
